/*
 * WebsiteCheckForChangeJob
 * Created: Dec 20, 2006 
 * by abargnesi<mailto:abargnesi@aquent.com/>
 */
package com.den4.jynform.tasks;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.SimpleNodeIterator;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.StatefulJob;

import com.den4.jynform.alerts.EmailLogger;

public class WebResourceCheckForChangeJob implements StatefulJob {
    private EmailLogger emailLogger;
    private String webResource;
    private List<NodeFilter> webFilters;
    private Map<NodeFilter,NodeList> origWebMatches;
    private Map<NodeFilter,NodeList> currentWebMatches;
    private String taskMsg;
    private String taskSubject;
    private SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a");
    private Log log = LogFactory.getLog(WebResourceCheckForChangeJob.class); 
    
    @SuppressWarnings("unchecked")
    public void execute(JobExecutionContext ctx) throws JobExecutionException {
        //Get needed data from stateful map.
        Map dataMap = ctx.getJobDetail().getJobDataMap();
        emailLogger = (EmailLogger) dataMap.get("emailLogger");
        webResource = (String) dataMap.get("webResource");
        webFilters = (List<NodeFilter>) dataMap.get("webFilters");
        origWebMatches =  (Map<NodeFilter,NodeList>) dataMap.get("webMatches");
        
        //Set subject for task.
        try {
            URL url = new URL(webResource);
            taskSubject = "WebResourceCheckForChangeJob for - ".concat(
                    url.getHost()).concat("(").concat(df.format(new Date()))
                    .concat(")");
        } catch (Exception e) {
            taskMsg = "The webResource(".concat(webResource).concat(") is invalid.");
            log.info(taskMsg);
            emailLogger.log(taskSubject, taskMsg);
        }
        
        //Parse html for web resource.  This requires a connection to be made
        //to this url.
        try {
            Parser p = new Parser(webResource);
            NodeList nodes = p.parse(null);
            currentWebMatches = new HashMap<NodeFilter,NodeList>();
            for(NodeFilter filter : webFilters) {
                NodeList nList = nodes.extractAllNodesThatMatch(filter, true);
                currentWebMatches.put(filter, nList);
            }
        } catch (Exception e) {
            taskMsg = "The webResource(".concat(webResource).concat(") errored while being parsed.");
            log.info(taskMsg);
            emailLogger.log(taskSubject, taskMsg);
            
            //After parsing error occurs, finish job.
            return;
        }
        
        //Do resource check between old web matches and current web matches.
        checkMatches();
        
        //Set current web matches to original web matches to save state.
        dataMap.put("webMatches", currentWebMatches);
    }

    @SuppressWarnings("unchecked")
    private void checkMatches() {
        if(origWebMatches != null) {
            log.info("Checking previous matches.");
            Set<NodeFilter> nodeFilterKeys = origWebMatches.keySet();
            Iterator<NodeFilter> it = nodeFilterKeys.iterator();
            while(it.hasNext()) {
                NodeFilter curFilter = it.next();
                NodeList old = origWebMatches.get(curFilter);
                NodeList current = currentWebMatches.get(curFilter);
                boolean nochange = compare(old, current);
                if(!nochange) {
                    //Send email regarding change.
                    taskMsg = "Web change has occured, consult the following difference:\n\n";
                    taskMsg = taskMsg.concat("Old NodeList\n").concat(old.toHtml(true)).concat("\n\n");
                    taskMsg = taskMsg.concat("Current NodeList\n").concat(current.toHtml(true));
                    log.info(taskMsg);
                    emailLogger.log(taskSubject, taskMsg);
                } else {
                    //Just print to console because there was no change.
                    taskMsg = "No change has occured with Node:\n\n";
                    taskMsg = taskMsg.concat(old.toHtml(true));
                    log.info(taskSubject);
                    log.info(taskMsg);
                }
            }
        } else {
            log.info("No checking, nothing previous.");
        }
    }
    
    /**
     * If there is any discrepancy (change) with nodes then return false.  Otherwise
     * return true (no change).  The checking is done by comparison of html.
     * 
     * @param oldList
     * @param newList
     * @return false if there is a change, true is there isn't
     */
    private boolean compare(NodeList oldList, NodeList newList) {
        SimpleNodeIterator oldNodeIt = oldList.elements();
        SimpleNodeIterator newNodeIt = newList.elements();
        while(oldNodeIt.hasMoreNodes() && newNodeIt.hasMoreNodes()) {
            String html0 = oldNodeIt.nextNode().toHtml(true);
            String html1 = newNodeIt.nextNode().toHtml(true);
            if(!html0.equals(html1)) {
                return false;
            }
        }
        return true;
    }
}
